
package com.cnksi.core.repository.springdata;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.jpa.domain.Specification;

import com.cnksi.core.repository.SearchFilter;
import com.cnksi.core.tools.utils.JLists;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;

@SuppressWarnings("all")
public class DynamicSpecifications
{

	private static final ConversionService conversionService = new DefaultConversionService();

	public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> filters, final Class<T> clazz)
	{

		return new Specification<T>()
		{

			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder)
			{

				if (JLists.isNotEmpty(filters))
				{

					List<Predicate> predicates = Lists.newArrayList();
					for (SearchFilter filter : filters)
					{

						// nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
						String[] names = StringUtils.split(filter.fieldName, ".");
						Path expression = root.get(names[0]);
						for (int i = 1; i < names.length; i++)
						{
							expression = expression.get(names[i]);
						}

						// convert value from string to target type
						Class attributeClass = expression.getJavaType();
						if (!attributeClass.equals(String.class) && filter.value instanceof String && conversionService.canConvert(String.class, attributeClass))
						{
							filter.value = conversionService.convert(filter.value, attributeClass);
						}

						// logic operator
						switch (filter.operator)
						{
						case EQ:
							predicates.add(builder.equal(expression, filter.value));
							break;
						case LIKE:
							predicates.add(builder.like(expression, "%" + filter.value + "%"));
							break;
						case GT:
							predicates.add(builder.greaterThan(expression, (Comparable) filter.value));
							break;
						case LT:
							predicates.add(builder.lessThan(expression, (Comparable) filter.value));
							break;
						case GTE:
							predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
							break;
						case LTE:
							predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));
							break;
						}
					}

					// 将所有条件用 and 联合起来
					if (predicates.size() > 0)
					{
						return builder.and(predicates.toArray(new Predicate[predicates.size()]));
					}
				}

				return builder.conjunction();
			}
		};
	}
}
